2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../jucer_Headers.h"
27 #include "jucer_JucerDocument.h"
28 #include "jucer_ObjectTypes.h"
29 #include "../ui/jucer_JucerDocumentHolder.h"
30 #include "components/jucer_ComponentUndoableAction.h"
33 //==============================================================================
34 ComponentLayout::ComponentLayout()
40 ComponentLayout::~ComponentLayout()
45 //==============================================================================
46 void ComponentLayout::changed()
52 void ComponentLayout::perform (UndoableAction
* action
, const String
& actionName
)
54 jassert (document
!= 0);
58 document
->getUndoManager().perform (action
, actionName
);
67 //==============================================================================
68 void ComponentLayout::clearComponents()
70 selected
.deselectAll();
75 //==============================================================================
76 class AddCompAction
: public UndoableAction
79 AddCompAction (XmlElement
* const xml_
, ComponentLayout
& layout_
)
94 Component
* const newComp
= layout
.addComponentFromXml (*xml
, false);
95 jassert (newComp
!= 0);
97 indexAdded
= layout
.indexOfComponent (newComp
);
98 jassert (indexAdded
>= 0);
99 return indexAdded
>= 0;
105 layout
.removeComponent (layout
.getComponent (indexAdded
), false);
109 int getSizeInUnits() { return 10; }
115 ComponentLayout
& layout
;
117 static void showCorrectTab()
119 JucerDocumentHolder
* const docHolder
= JucerDocumentHolder::getActiveDocumentHolder();
122 docHolder
->showLayout();
125 AddCompAction (const AddCompAction
&);
126 AddCompAction
& operator= (const AddCompAction
&);
129 //==============================================================================
130 class DeleteCompAction
: public ComponentUndoableAction
<Component
>
133 DeleteCompAction (Component
* const comp
, ComponentLayout
& layout
)
134 : ComponentUndoableAction
<Component
> (comp
, layout
),
137 ComponentTypeHandler
* const h
= ComponentTypeHandler::getHandlerFor (*comp
);
140 xml
= h
->createXmlFor (comp
, &layout
);
144 oldIndex
= layout
.indexOfComponent (comp
);
155 layout
.removeComponent (getComponent(), false);
161 Component
* c
= layout
.addComponentFromXml (*xml
, false);
164 layout
.moveComponentZOrder (layout
.indexOfComponent (c
), oldIndex
);
175 void ComponentLayout::removeComponent (Component
* comp
, const bool undoable
)
177 if (comp
!= 0 && components
.contains (comp
))
181 perform (new DeleteCompAction (comp
, *this), "Delete components");
185 selected
.deselect (comp
);
186 selected
.changed (true); // synchronous message to get rid of any property components
188 components
.removeObject (comp
);
194 //==============================================================================
195 class FrontBackCompAction
: public ComponentUndoableAction
<Component
>
198 FrontBackCompAction (Component
* const comp
, ComponentLayout
& layout
, int newIndex_
)
199 : ComponentUndoableAction
<Component
> (comp
, layout
),
202 oldIndex
= layout
.indexOfComponent (comp
);
208 Component
* comp
= layout
.getComponent (oldIndex
);
209 layout
.moveComponentZOrder (oldIndex
, newIndex
);
210 newIndex
= layout
.indexOfComponent (comp
);
217 layout
.moveComponentZOrder (newIndex
, oldIndex
);
222 int newIndex
, oldIndex
;
225 void ComponentLayout::moveComponentZOrder (int oldIndex
, int newIndex
)
227 jassert (components
[oldIndex
] != 0);
229 if (oldIndex
!= newIndex
&& components
[oldIndex
] != 0)
231 components
.move (oldIndex
, newIndex
);
236 void ComponentLayout::componentToFront (Component
* comp
, const bool undoable
)
238 if (comp
!= 0 && components
.contains (comp
))
241 perform (new FrontBackCompAction (comp
, *this, -1), "Move components to front");
243 moveComponentZOrder (components
.indexOf (comp
), -1);
247 void ComponentLayout::componentToBack (Component
* comp
, const bool undoable
)
249 if (comp
!= 0 && components
.contains (comp
))
252 perform (new FrontBackCompAction (comp
, *this, 0), "Move components to back");
254 moveComponentZOrder (components
.indexOf (comp
), 0);
259 //==============================================================================
260 const char* const ComponentLayout::clipboardXmlTag
= "COMPONENTS";
262 void ComponentLayout::copySelectedToClipboard()
264 if (selected
.getNumSelected() == 0)
267 XmlElement
clip (clipboardXmlTag
);
269 for (int i
= 0; i
< components
.size(); ++i
)
271 Component
* const c
= components
.getUnchecked(i
);
273 if (selected
.isSelected (c
))
275 ComponentTypeHandler
* const type
= ComponentTypeHandler::getHandlerFor (*c
);
280 XmlElement
* const e
= type
->createXmlFor (c
, this);
281 clip
.addChildElement (e
);
286 SystemClipboard::copyTextToClipboard (clip
.createDocument (String::empty
, false, false));
289 void ComponentLayout::paste()
291 XmlDocument
clip (SystemClipboard::getTextFromClipboard());
292 XmlElement
* const doc
= clip
.getDocumentElement();
294 if (doc
!= 0 && doc
->hasTagName (clipboardXmlTag
))
296 selected
.deselectAll();
298 forEachXmlChildElement (*doc
, e
)
300 Component
* newComp
= addComponentFromXml (*e
, true);
303 selected
.addToSelection (newComp
);
307 dragSelectedComps (Random::getSystemRandom().nextInt (40),
308 Random::getSystemRandom().nextInt (40));
315 void ComponentLayout::deleteSelected()
317 const SelectedItemSet
<Component
*> temp (selected
);
318 selected
.deselectAll();
319 selected
.changed (true); // synchronous message to get rid of any property components
321 if (temp
.getNumSelected() > 0)
323 for (int i
= temp
.getNumSelected(); --i
>= 0;)
324 removeComponent (temp
.getSelectedItem (i
), true);
329 document
->dispatchPendingMessages(); // forces the change to propagate before a paint() callback can happen,
330 // in case there are components floating around that are now dangling pointers
334 void ComponentLayout::selectAll()
336 for (int i
= 0; i
< components
.size(); ++i
)
337 selected
.addToSelection (components
.getUnchecked (i
));
340 void ComponentLayout::selectedToFront()
342 const SelectedItemSet
<Component
*> temp (selected
);
344 for (int i
= temp
.getNumSelected(); --i
>= 0;)
345 componentToFront (temp
.getSelectedItem(i
), true);
348 void ComponentLayout::selectedToBack()
350 const SelectedItemSet
<Component
*> temp (selected
);
352 for (int i
= 0; i
< temp
.getNumSelected(); ++i
)
353 componentToBack (temp
.getSelectedItem(i
), true);
356 void ComponentLayout::bringLostItemsBackOnScreen (int width
, int height
)
358 for (int i
= components
.size(); --i
>= 0;)
360 Component
* const c
= components
[i
];
362 if (! c
->getBounds().intersects (Rectangle
<int> (0, 0, width
, height
)))
364 c
->setTopLeftPosition (width
/ 2, height
/ 2);
365 updateStoredComponentPosition (c
, false);
370 Component
* ComponentLayout::addNewComponent (ComponentTypeHandler
* const type
, int x
, int y
)
372 Component
* c
= type
->createNewComponent (getDocument());
377 c
->setSize (type
->getDefaultWidth(), type
->getDefaultHeight());
378 c
->setCentrePosition (x
, y
);
379 updateStoredComponentPosition (c
, false);
381 c
->getProperties().set ("id", nextCompUID
++);
383 XmlElement
* xml
= type
->createXmlFor (c
, this);
386 c
= addComponentFromXml (*xml
, true);
389 String
memberName (makeValidCppIdentifier (type
->getClassName (c
), true, true, false));
390 setComponentMemberVariableName (c
, memberName
);
392 selected
.selectOnly (c
);
399 Component
* ComponentLayout::addComponentFromXml (const XmlElement
& xml
, const bool undoable
)
403 AddCompAction
* const action
= new AddCompAction (new XmlElement (xml
), *this);
404 perform (action
, "Add new components");
406 return components
[action
->indexAdded
];
410 ComponentTypeHandler
* const type
411 = ComponentTypeHandler::getHandlerForXmlTag (xml
.getTagName());
415 Component
* const newComp
= type
->createNewComponent (getDocument());
417 if (type
->restoreFromXml (xml
, newComp
, this))
419 // ensure that the new comp's name is unique
420 setComponentMemberVariableName (newComp
, getComponentMemberVariableName (newComp
));
422 // check for duped IDs..
423 while (findComponentWithId (ComponentTypeHandler::getComponentId (newComp
)) != 0)
424 ComponentTypeHandler::setComponentId (newComp
, Random::getSystemRandom().nextInt64());
426 components
.add (newComp
);
440 Component
* ComponentLayout::findComponentWithId (const int64 componentId
) const
442 for (int i
= 0; i
< components
.size(); ++i
)
443 if (ComponentTypeHandler::getComponentId (components
.getUnchecked(i
)) == componentId
)
444 return components
.getUnchecked(i
);
449 //==============================================================================
450 static const char* const dimensionSuffixes
[] = { "X", "Y", "W", "H" };
452 Component
* ComponentLayout::getComponentRelativePosTarget (Component
* comp
, int whichDimension
) const
456 PaintElement
* const pe
= dynamic_cast <PaintElement
*> (comp
);
462 if (whichDimension
== 0)
463 compId
= pe
->getPosition().relativeToX
;
464 else if (whichDimension
== 1)
465 compId
= pe
->getPosition().relativeToY
;
466 else if (whichDimension
== 2)
467 compId
= pe
->getPosition().relativeToW
;
469 compId
= pe
->getPosition().relativeToH
;
471 return findComponentWithId (compId
);
475 return findComponentWithId (comp
->getProperties() [String ("relativeTo") + dimensionSuffixes
[whichDimension
]]
476 .toString().getHexValue64());
480 void ComponentLayout::setComponentRelativeTarget (Component
* comp
, int whichDimension
, Component
* compToBeRelativeTo
)
482 PaintElement
* const pe
= dynamic_cast <PaintElement
*> (comp
);
485 jassert (pe
!= 0 || components
.contains (comp
));
486 jassert (compToBeRelativeTo
== 0 || components
.contains (compToBeRelativeTo
));
487 jassert (compToBeRelativeTo
== 0 || ! dependsOnComponentForRelativePos (compToBeRelativeTo
, comp
));
489 if (compToBeRelativeTo
!= getComponentRelativePosTarget (comp
, whichDimension
)
490 && (compToBeRelativeTo
== 0 || ! dependsOnComponentForRelativePos (compToBeRelativeTo
, comp
)))
492 const int64 compId
= ComponentTypeHandler::getComponentId (compToBeRelativeTo
);
494 Rectangle
<int> oldBounds (comp
->getBounds());
495 RelativePositionedRectangle pos
;
499 oldBounds
= pe
->getCurrentBounds (dynamic_cast <PaintRoutineEditor
*> (pe
->getParentComponent())->getComponentArea());
500 pos
= pe
->getPosition();
504 pos
= ComponentTypeHandler::getComponentPosition (comp
);
507 if (whichDimension
== 0)
508 pos
.relativeToX
= compId
;
509 else if (whichDimension
== 1)
510 pos
.relativeToY
= compId
;
511 else if (whichDimension
== 2)
512 pos
.relativeToW
= compId
;
513 else if (whichDimension
== 3)
514 pos
.relativeToH
= compId
;
518 pe
->setPosition (pos
, true);
519 pe
->setCurrentBounds (oldBounds
, dynamic_cast <PaintRoutineEditor
*> (pe
->getParentComponent())->getComponentArea(), true);
523 setComponentPosition (comp
, pos
, true);
524 comp
->setBounds (oldBounds
);
525 updateStoredComponentPosition (comp
, false);
532 bool ComponentLayout::dependsOnComponentForRelativePos (Component
* comp
, Component
* possibleDependee
) const
534 for (int i
= 0; i
< 4; ++i
)
536 Component
* const c
= getComponentRelativePosTarget (comp
, i
);
537 if (c
!= 0 && (c
== possibleDependee
|| dependsOnComponentForRelativePos (c
, possibleDependee
)))
544 const int menuIdBase
= 0x63240000;
546 PopupMenu
ComponentLayout::getRelativeTargetMenu (Component
* comp
, int whichDimension
) const
550 Component
* const current
= getComponentRelativePosTarget (comp
, whichDimension
);
552 m
.addItem (menuIdBase
, "Relative to parent component", true, current
== 0);
555 for (int i
= 0; i
< components
.size(); ++i
)
557 Component
* const c
= components
.getUnchecked(i
);
561 m
.addItem (menuIdBase
+ i
+ 1,
562 "Relative to " + getComponentMemberVariableName (c
)
563 + " (class: " + ComponentTypeHandler::getHandlerFor (*c
)->getClassName (c
) + ")",
564 ! dependsOnComponentForRelativePos (c
, comp
),
572 void ComponentLayout::processRelativeTargetMenuResult (Component
* comp
, int whichDimension
, int menuResultID
)
574 if (menuResultID
!= 0)
576 Component
* const newTarget
= components
[menuResultID
- menuIdBase
- 1];
577 setComponentRelativeTarget (comp
, whichDimension
, newTarget
);
581 //==============================================================================
582 class ChangeCompPositionAction
: public ComponentUndoableAction
<Component
>
585 ChangeCompPositionAction (Component
* const comp
, ComponentLayout
& layout
,
586 const RelativePositionedRectangle
& newPos_
)
587 : ComponentUndoableAction
<Component
> (comp
, layout
),
590 oldPos
= ComponentTypeHandler::getComponentPosition (comp
);
596 layout
.setComponentPosition (getComponent(), newPos
, false);
603 layout
.setComponentPosition (getComponent(), oldPos
, false);
608 RelativePositionedRectangle newPos
, oldPos
;
611 void ComponentLayout::setComponentPosition (Component
* comp
,
612 const RelativePositionedRectangle
& newPos
,
615 if (ComponentTypeHandler::getComponentPosition (comp
) != newPos
)
619 perform (new ChangeCompPositionAction (comp
, *this, newPos
), "Move components");
623 ComponentTypeHandler::setComponentPosition (comp
, newPos
, this);
629 void ComponentLayout::updateStoredComponentPosition (Component
* comp
, const bool undoable
)
631 RelativePositionedRectangle
newPos (ComponentTypeHandler::getComponentPosition (comp
));
633 newPos
.updateFromComponent (*comp
, this);
635 setComponentPosition (comp
, newPos
, undoable
);
638 //==============================================================================
639 void ComponentLayout::startDragging()
641 for (int i
= 0; i
< components
.size(); ++i
)
643 Component
* const c
= components
[i
];
644 c
->getProperties().set ("xDragStart", c
->getX());
645 c
->getProperties().set ("yDragStart", c
->getY());
648 jassert (document
!= 0);
649 document
->getUndoManager().beginNewTransaction();
652 void ComponentLayout::dragSelectedComps (int dx
, int dy
, const bool allowSnap
)
654 if (allowSnap
&& document
!= 0 && selected
.getNumSelected() > 1)
656 dx
= document
->snapPosition (dx
);
657 dy
= document
->snapPosition (dy
);
660 for (int i
= 0; i
< selected
.getNumSelected(); ++i
)
662 Component
* const c
= selected
.getSelectedItem (i
);
664 const int startX
= c
->getProperties() ["xDragStart"];
665 const int startY
= c
->getProperties() ["yDragStart"];
667 if (allowSnap
&& document
!= 0 && selected
.getNumSelected() == 1)
669 c
->setTopLeftPosition (document
->snapPosition (startX
+ dx
),
670 document
->snapPosition (startY
+ dy
));
674 c
->setTopLeftPosition (startX
+ dx
,
678 updateStoredComponentPosition (c
, false);
682 void ComponentLayout::endDragging()
684 // after the drag, roll back all the comps to their start position, then
685 // back to their finish positions using an undoable command.
686 document
->getUndoManager().beginNewTransaction();
688 for (int i
= 0; i
< selected
.getNumSelected(); ++i
)
690 Component
* const c
= selected
.getSelectedItem (i
);
692 const int newX
= c
->getX();
693 const int newY
= c
->getY();
695 const int startX
= c
->getProperties() ["xDragStart"];
696 const int startY
= c
->getProperties() ["yDragStart"];
698 c
->setTopLeftPosition (startX
, startY
);
699 updateStoredComponentPosition (c
, false);
701 c
->setTopLeftPosition (newX
, newY
);
702 updateStoredComponentPosition (c
, true);
705 document
->getUndoManager().beginNewTransaction();
708 void ComponentLayout::moveSelectedComps (int dx
, int dy
, bool snap
)
711 dragSelectedComps (dx
, dy
, snap
);
715 void ComponentLayout::stretchSelectedComps (int dw
, int dh
, bool allowSnap
)
719 if (document
!= 0 && selected
.getNumSelected() == 1)
721 Component
* const c
= selected
.getSelectedItem (0);
725 int bot
= c
->getBottom() + dh
;
726 int right
= c
->getRight() + dw
;
727 bot
= (dh
!= 0) ? document
->snapPosition (bot
) : bot
;
728 right
= (dw
!= 0) ? document
->snapPosition (right
) : right
;
729 newh
= bot
- c
->getY();
730 neww
= right
- c
->getX();
734 newh
= c
->getHeight() + dh
;
735 neww
= c
->getWidth() + dw
;
738 c
->setSize (neww
, newh
);
740 updateStoredComponentPosition (c
, true);
744 for (int i
= 0; i
< selected
.getNumSelected(); ++i
)
746 Component
* const c
= selected
.getSelectedItem (i
);
748 neww
= c
->getWidth() + dw
;
749 newh
= c
->getHeight() + dh
;
750 c
->setSize (neww
, newh
);
752 updateStoredComponentPosition (c
, true);
758 //==============================================================================
759 void ComponentLayout::fillInGeneratedCode (GeneratedCode
& code
) const
761 for (int i
= 0; i
< components
.size(); ++i
)
763 Component
* const comp
= components
[i
];
764 ComponentTypeHandler
* const type
= ComponentTypeHandler::getHandlerFor (*comp
);
767 type
->fillInGeneratedCode (comp
, code
);
771 //==============================================================================
772 const String
ComponentLayout::getComponentMemberVariableName (Component
* comp
) const
775 return String::empty
;
777 String
name (comp
->getProperties() ["memberName"].toString());
780 name
= getUnusedMemberName (makeValidCppIdentifier (comp
->getName(), true, true, false), comp
);
785 void ComponentLayout::setComponentMemberVariableName (Component
* comp
, const String
& newName
)
787 const String
oldName (getComponentMemberVariableName (comp
));
789 comp
->getProperties().set ("memberName", String::empty
);
791 const String
n (getUnusedMemberName (makeValidCppIdentifier (newName
, false, true, false), comp
));
792 comp
->getProperties().set ("memberName", n
);
798 const String
ComponentLayout::getUnusedMemberName (String nameRoot
, Component
* comp
) const
802 while (CharacterFunctions::isDigit (nameRoot
.getLastCharacter()))
803 nameRoot
= nameRoot
.dropLastCharacters (1);
809 bool alreadyUsed
= false;
811 for (int i
= 0; i
< components
.size(); ++i
)
813 if (components
[i
] != comp
814 && components
[i
]->getProperties() ["memberName"] == n
)
824 n
= nameRoot
+ String (suffix
++);
830 //==============================================================================
831 const String
ComponentLayout::getComponentVirtualClassName (Component
* comp
) const
834 return String::empty
;
836 return comp
->getProperties() ["virtualName"];
839 void ComponentLayout::setComponentVirtualClassName (Component
* comp
, const String
& newName
)
841 const String
name (makeValidCppIdentifier (newName
, false, false, true));
843 if (name
!= getComponentVirtualClassName (comp
))
845 comp
->getProperties().set ("virtualName", name
);
850 //==============================================================================
851 void ComponentLayout::addToXml (XmlElement
& xml
) const
853 for (int i
= 0; i
< components
.size(); ++i
)
855 ComponentTypeHandler
* h
= ComponentTypeHandler::getHandlerFor (*components
[i
]);
858 xml
.addChildElement (h
->createXmlFor (components
[i
], this));